package com.zc.common.generate.service.impl;

import com.zc.common.generate.dto.EntityIdConf;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.Assert;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
public class SegmentNode {

    /**
     * 当前值
     */
    private Long current;

    /**
     * 当前batch里的最大值
     */
    private Long max;
    /**
     * 前缀
     */
    private String prefix;


    /**
     * 日期格式
     */
    private String datePattern;


    /**
     * 就是生产的具体日期格式，20221111,缓存值
     */
    private String datePrefix;


    /**
     * 是否带业务前缀idCode
     */
    private boolean includeBizPrefix;

    /**
     * 流水需要的位数
     */
    private int numBit;

    /**
     * 每次步长
     */
    private int step;


    /**
     * 最后生成的id，存储数据库查看
     */
    private String lastGeneratedId = "";

    private String idCode;

    /**
     * 用于获取流水号加锁
     */
    private final ReentrantLock lock = new ReentrantLock(true);

    private static final int RETRY_COUNT = 5;

    private SegmentNodeService segmentNodeService;

    public SegmentNode(SegmentNodeService segmentNodeService) {
        segmentNodeService = segmentNodeService;
    }


    /**
     * 用于获取与更新数据id资源
     *
     * @param retryCount
     */
    private void loadFromDB(int retryCount) {
        boolean success = false;
        if (retryCount <= 0) {
            return;
        }
        try {
            EntityIdConf entityIdConf = segmentNodeService.getByIdCode(this.idCode);
            //检查如果日期前缀变化
            String tempDatePrefix = this.getTempDatePrefix(this.datePattern);
            if (StringUtils.isNotBlank(tempDatePrefix) && !StringUtils.equals(tempDatePrefix, entityIdConf.getDatePrefix())) {
                entityIdConf.setDatePrefix(tempDatePrefix);
                //日期发生变化则，从0开发计算
                entityIdConf.setNextBatchMax(entityIdConf.getStep() + 1L);
            } else {
                //如果日期前缀没有变化，则增加步长,(在服务重启时这将损失id资源)
                entityIdConf.setNextBatchMax(entityIdConf.getNextBatchMax() + entityIdConf.getStep());
            }
            entityIdConf.setLastGeneratedId(this.lastGeneratedId);
            //先更新数据库，再更新内存。
            if (segmentNodeService.updateNextBatch(entityIdConf) >= 0) {
                updateValueFromDB(entityIdConf);
            }
            success = true;

        } catch (Exception e) {
            log.error("", e);
        }

        if (!success) {
            try {
                Thread.sleep(75);
            } catch (InterruptedException e) {
                log.error("", e);
            }
            loadFromDB(retryCount - 1);
        }
    }


    private void updateValueFromDB(EntityIdConf entityIdConf) {
        if (entityIdConf == null) {
            return;
        }
        if (StringUtils.isNotBlank(entityIdConf.getDatePrefix())) {
            this.datePrefix = entityIdConf.getDatePrefix();
        }
        this.numBit = entityIdConf.getNumBit();
//        this.includeBizPrefix=entityIdConf.get
        this.datePattern = entityIdConf.getDatePattern();
        this.datePrefix = entityIdConf.getFixPrefix();
        this.max = entityIdConf.getNextBatchMax() - 1L;
        this.current = entityIdConf.getNextBatchMax() - entityIdConf.getStep();
    }


    public boolean hasNext() {
        if (current + 1 > max) {
            return false;
        }
        return true;
    }

    public String getNextId(String bizCode) {
        lock.lock();
        try {
            String tranNo = getNextTranNo();
            String nextId = this.prefix + (this.includeBizPrefix ? bizCode : "")
                    + (StringUtils.isNotBlank(this.datePrefix) ? this.datePrefix : "")
                    + tranNo;
            return nextId;
        } finally {
            lock.unlock();
        }
    }

    public List<String> getNextEntityIds(String bizCode, Integer reqCount) {
        Assert.isTrue(reqCount > 0, "reqCount必须大于0");
        lock.lock();
        try {
            List<String> list = new ArrayList<>();
            for (int i = 0; i < reqCount; i++) {
                list.add(getNextId(bizCode));
            }
            return list;
        } finally {
            lock.unlock();
        }
    }


    private String getNextTranNo() {
        //检查如果日期前缀变化
        String tempDatePrefix = this.getTempDatePrefix(this.datePattern);
        if (StringUtils.isNotBlank(tempDatePrefix) && !StringUtils.equals(tempDatePrefix, this.datePrefix)) {
            this.loadFromDB(RETRY_COUNT);
        }
        //如果内存id资源耗光,需要同步数据库
        if (current + 1 > max) {
            this.loadFromDB(RETRY_COUNT);
        }

        if (current + 1 <= max) {
            current++;
        }
        String nextVal = Long.toString(current);
        return StringUtils.leftPad(nextVal, this.numBit, "0");
    }


    private String getTempDatePrefix(String datePattern) {
        if (datePattern.trim().equalsIgnoreCase("")) {
            return "";
        }
        LocalDate localDate = LocalDate.now();
        return localDate.format(DateTimeFormatter.ofPattern(datePattern));
    }
}
